/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Client;

/**
 *
 * @author j_sno bujhbjh
 */
//TODO implement GUI in separate thread so that it doesn't block the client
import gui.*;
import java.net.*;
import java.io.IOException;
import java.lang.String;
import com.google.gson.Gson;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Client {

    private static final int TX_BUFFER = 4096;
    Gson gson;
    DatagramSocket RXsocket, TXsocket;
    InetAddress serverAddress;
    int serverPort;
    String clientName;
    int requestCounter;
    public FIFOQueue<Message> requestQ;
    public ArrayList<Item> itemList;
    public ArrayList<Item> myItemList;
    Login loginWindowHandle;
    MainUserPage mainUserPageHandle;

    public Client(int clientPort){

        System.out.println("Initializing client at --->" + clientPort);

        gson = new Gson();
        
        if(clientPort == 0){
            try {
                RXsocket = new DatagramSocket();
                TXsocket = new DatagramSocket(RXsocket.getLocalPort()+1);
            } catch (SocketException ex) {
                Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
            }
        
        }else{
            //int sockets
            try {
                RXsocket = new DatagramSocket(clientPort);
                TXsocket = new DatagramSocket(clientPort+1);
            } catch (SocketException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
        }

        itemList = new ArrayList<Item>();
        myItemList = new ArrayList<Item>();
        requestQ = new FIFOQueue<Message>();
        requestCounter = 0;
        initClient();
    }

    public void sendRegister(String Name, String Address, int Port) throws UnknownHostException {
        
        System.out.println("Registering Client as --- >" + Name + "\nWith Server at --- >" + Address + ":" + Port);
        clientName = Name;
        serverAddress = InetAddress.getByName(Address);
        serverPort = Port;

        Message message = new Message();
        message = new Message("REGISTER");
        message.type = "REGISTER";
        message.name = this.clientName;
        try {
            encodeAndSend(message);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public void sendDeRegister() {

        Message message = new Message();
        message = new Message("DEREGISTER");
        message.type = "DEREGISTER";
        message.name = this.clientName;
        message.ipAddress = serverAddress.toString();
        try {
            encodeAndSend(message);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    void encodeAndSend(Message Obj) throws IOException {

        if(RXsocket.getLocalAddress() == null){
            System.out.println("the socket is null !!!");
            return;
        }
        requestCounter++;
        InetAddress adr = RXsocket.getLocalAddress();
        Obj.rqId = requestCounter;

        System.out.println("RXsocket address --- > "+RXsocket.getLocalAddress().toString());
        Obj.ipAddress = adr.toString();
        Obj.port = RXsocket.getLocalPort();
        String outgoingMessage = gson.toJson(Obj);
        System.out.println("sending : " + outgoingMessage);
        byte[] outputdata = outgoingMessage.getBytes();
        DatagramPacket packet = new DatagramPacket(outputdata, outputdata.length, serverAddress, serverPort);
        TXsocket.connect(serverAddress, serverPort);
        TXsocket.send(packet);
        TXsocket.disconnect();
        
    }

    public void offerMessage(String Description, String min_amount) {

        Message message = new Message();
        message = new Message("OFFER");
        message.type = "OFFER";
        message.name = this.clientName;
        message.description = Description;
        message.bid = min_amount;
        try {
            encodeAndSend(message);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public void sendBidMessage(String min_amount, String itemId) {

        Message message = new Message();
        message = new Message("BID");
        message.type = "BID";
        message.itemId = itemId;
        message.name = this.clientName;
        message.bid = min_amount;

        try {
            encodeAndSend(message);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public Message recieve() throws IOException {

        //block on socket for an incoming message;
        Message message = new Message();
        byte[] data = new byte[TX_BUFFER];
        DatagramPacket packet = new DatagramPacket(data, data.length);
        RXsocket.setSoTimeout(3000);
        System.out.println("Listening on ---> "+RXsocket.getLocalSocketAddress().toString());
        RXsocket.receive(packet);
        String rx = new String(packet.getData(), "UTF-8");
        String incomingMessage = rx.trim();
        System.out.println("recieved message : " + incomingMessage + rx);
        message = gson.fromJson(incomingMessage, Message.class);
        if(message != null){
            message.ipAddress = packet.getAddress().toString();
            message.port = packet.getPort();
            System.out.println("recieved message : " + incomingMessage + rx);
            return message;
        }else{
            System.out.println("bad packet recived ... ");
            return null;
        }
    }

    public void listenerThread() {
        // TODO Auto-generated method stub

        //thread1 receive loop
        //save shit in queue
        while (true) {
            Message incomingMessage = new Message();
            incomingMessage.type = null;
            try {

                incomingMessage = this.recieve();
                if(incomingMessage == null){
                    System.out.println("Recieved a null message");
                    continue;
                }

            } catch (IOException e) {
                // TODO Auto-generated catch block
                // e.printStackTrace();
                System.out.println(e.getMessage());
                //break;
            }
            if (incomingMessage.type != null) {
                //add stuff to FIFO queue
                System.out.println("Adding " + incomingMessage.type + " to requestQ");
                this.requestQ.put(incomingMessage);
            }
        }

    }

    public void getitemlist() {

        Message message = new Message();
        message = new Message("GETITEMS");
        message.type = "GETITEMS";
        //message.name = this.clientName;
        try {
            encodeAndSend(message);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
    
    private boolean isInItems(String itemId, ArrayList<Item> itemsList){
    
        for(Item item : itemsList){

            if(itemId.equals(item.itemId)){
                return true;
            }
        }
        return false;
    }
    
    private void removeItem(String itemId, ArrayList<Item> itemsList){
    
        ArrayList<Item> itemsToRemove = new ArrayList<Item>();
        
        for(Item item : itemsList){

            if(itemId.equals(item.itemId)){
                itemsToRemove.add(item);
            }
        }
        
        for(Item item:itemsToRemove){
            
            itemsList.remove(item);
        }
    }
    
    private Item getItem(String itemId, ArrayList<Item> itemsList){
    
        for(Item item : itemsList){

            if(itemId.equals(item.itemId)){
                return item;
            }
        }
        System.out.println("Item that was not the list");
        return new Item("1234","it was not found",Float.valueOf("9001"));
    }

    public void requestHandler(Message recObj) {

        if ("UNREGISTERED".equals(recObj.type)) {

            System.out.println(recObj.reason);

            PopUp unreg = new PopUp();
            unreg.messagetouser = "Registration was unsucessful try again ?";
            unreg.DisplayMessage();

        } else if ("DEREG_CONF".equals(recObj.type)) {

            System.out.println("You have succesfully left the aution");

            PopUp deregconf = new PopUp();
            deregconf.messagetouser = "You have succesfully left the aution. Click OK to exit";
            deregconf.DisplayMessage();

            System.exit(0);

        } else if ("DEREG_DENIED".equals(recObj.type)) {

            System.out.println("De-Registration Denied");
            PopUp deregdenied = new PopUp();
            deregdenied.messagetouser = recObj.reason;
            deregdenied.DisplayMessage();

        } else if ("REGISTERED".equals(recObj.type)) {

            final Client clientobj = this;
            System.out.println("Registration was sucessful!!");

            final MainUserPage dialog = new MainUserPage(new javax.swing.JFrame(), true, clientobj);
            clientobj.mainUserPageHandle = dialog;

            //invoke main page Gui
            java.awt.EventQueue.invokeLater(new Runnable() {

                @Override
                public void run() {

                    //Add window listner for event click close window button
                    dialog.addWindowListener(new java.awt.event.WindowAdapter() {

                        @Override
                        public void windowClosing(java.awt.event.WindowEvent e) {
                            System.exit(0);
                        }
                    });
                    dialog.setVisible(true);

                }
            });

        } else if ("ITEMLIST".equals(recObj.type)) {
            this.itemList.clear();
            this.itemList = recObj.items;
            
            /*Update the GUI*/
            java.awt.EventQueue.invokeLater(new Runnable() {

            @Override
                public void run() {
                    mainUserPageHandle.updateTable(itemList);
                }
            });
            
        }else if("NEW-ITEM".equals(recObj.type)){
            
            if(isInItems(recObj.itemId,itemList)){
            
                System.out.println("user did'nt have the item");
                
            }else if(isInItems(recObj.itemId,myItemList)){
            
                System.out.println("user is selling this item");

            }else{
            
                this.itemList.add(new Item(recObj.itemId, recObj.description, Float.valueOf(recObj.bid)));
                /*Update the GUI*/
                java.awt.EventQueue.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        mainUserPageHandle.updateTable(itemList);
                    }
                });
            
            }
            
        }else if("OFFER-CONF".equals(recObj.type)){
        
            this.myItemList.add(new Item(recObj.itemId, recObj.description, Float.valueOf(recObj.bid)));
            /*Update the GUI*/
            java.awt.EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    mainUserPageHandle.updateMyTable(myItemList);
                }
            });
        
            
        }
        
        else if("WIN".equals(recObj.type)){
            
            Item winItem = getItem(recObj.itemId,itemList);
        
            removeItem(recObj.itemId, itemList);
            removeItem(recObj.itemId, myItemList);
            
            PopUp deregconf = new PopUp();
            deregconf.messagetouser = "You have Won Item"+winItem.description+" for "+recObj.bid;
            deregconf.DisplayMessage();
            
            /*Update the GUI*/
            java.awt.EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    mainUserPageHandle.updateMyTable(myItemList);
                    mainUserPageHandle.updateTable(itemList);
                }
            });
          
        }
        
        else if("SOLDTO".equals(recObj.type)){
            
            Item soldItem = getItem(recObj.itemId,myItemList);
        
            removeItem(recObj.itemId, itemList);
            removeItem(recObj.itemId, myItemList);
            
            PopUp deregconf = new PopUp();
            deregconf.messagetouser = "You have Sold Item"+soldItem.description+" for "+recObj.bid;
            deregconf.DisplayMessage();
            
            /*Update the GUI*/
            java.awt.EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    mainUserPageHandle.updateMyTable(myItemList);
                    mainUserPageHandle.updateTable(itemList);
                }
            });
          
        }
        
        else if("BID-OVER".equals(recObj.type)){
            
            removeItem(recObj.itemId, itemList);
            removeItem(recObj.itemId, myItemList);
            
            /*Update the GUI*/
            java.awt.EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    mainUserPageHandle.updateMyTable(myItemList);
                }
            });
            
            /*Update the GUI*/
            java.awt.EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    mainUserPageHandle.updateTable(itemList);
                }
            });
        
            
        }
        else if ("HIGHEST".equals(recObj.type)) {

            for(Item j : myItemList){
                
                    if(j.itemId.equals(recObj.itemId)){
                        
                       if (j.currentBid <= Float.valueOf(recObj.bid)) {
                            
                            j.currentBid = Float.valueOf(recObj.bid);
                            /*Update the GUI*/
                            java.awt.EventQueue.invokeLater(new Runnable() {

                                @Override
                                public void run() {
                                    mainUserPageHandle.updateMyTable(myItemList);
                                }
                            });

                        }else{
                            System.out.println("Out of sync with Server, Current Bid > Highest Msg");
                        }
                    
                    }
                    
            for (Item i : itemList) {
                
                if(i.itemId.equals(recObj.itemId)) {
                    if (i.currentBid <= Float.valueOf(recObj.bid)) {
                        i.currentBid = Float.valueOf(recObj.bid);
                        /*Update the GUI*/
                        java.awt.EventQueue.invokeLater(new Runnable() {

                            @Override
                            public void run() {
                                mainUserPageHandle.updateTable(itemList);
                            }
                        });
                        
                    }else{
                        System.out.println("Out of sync with Server, Current Bid > Highest Msg");
                    }
                }else{
                    System.out.println("Out of sync with Server, Item not found");
                }
           }
          

    }

        } else {

            System.out.println("Unknown message recieved");
            System.out.println("wtf ?? ---> " + recObj.type);
        }

    }
   
    public void clientLogin(){
        
        /* Create and display the GUI */
        final Login loginDialog = new Login(this);
        java.awt.EventQueue.invokeLater(new Runnable() {
        
            @Override
            public void run() {
                loginDialog.setVisible(true);
            }
        });
    
    }
    
    public void initClient() {
        
              //requestHandler thread
        new Thread(
                new Runnable() {

                    @Override
                    public void run() {
                        System.out.println("requestHandler started.--->" + this.getClass());
                        //if(client != null){
                            requestHandlerThread();
                        //}
                    }
                }).start();
        
        //listener thread
            new Thread(
                new Runnable() {

                    @Override
                    public void run() {
                        System.out.println("Listener started.--->" + this.getClass());
                        //if(client != null){
                            listenerThread();
                        //}
                    }
                }).start();
            
         clientLogin();       
        
    }

    public void requestHandlerThread() {

        while (true) {

            if (requestQ.isEmpty()) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    System.out.println(e.getMessage());
                }
            } else {
                System.out.println("handling request --->" + requestQ.peek().type + " from " + requestQ.peek().ipAddress + " : " + requestQ.peek().port);
                requestHandler(requestQ.get());


            }
        }
    }
}
